#include "fxcc/core/graph/opengl3/RenderStates.h"

using namespace fxcc::graph::opengl3;
using namespace fxcc::graph::common;

int fxcc::graph::opengl3::RenderStates::GetFormat(int numChannel)
{
	return m_FormatDescs[numChannel].m_Format;
}

int fxcc::graph::opengl3::RenderStates::GetInternalFomart(int numChannel, int hdr)
{
	int res = 0;

	switch (hdr)
	{
	case 0:
		res = m_FormatDescs[numChannel].m_InternalFormat;
		break;
	case 1:
		res = m_FormatDescs[numChannel].m_HdrInternalFormat16F;
		break;
	case 2:
		res = m_FormatDescs[numChannel].m_HdrInternalFormat32F;
		break;
	default:
		break;
	}
	return res;
}

fxcc::graph::opengl3::SamplerTypeDesc fxcc::graph::opengl3::RenderStates::GetSamplerTypeDesc(const fxcc::graph::common::SamplerType &type)
{
	return m_SamplerDescs[(int)type];
}

VertexTypeDesc fxcc::graph::opengl3::RenderStates::GetVertiexDesc(const common::VertexType &type)
{
	return m_VertexDescs[(int)type];
}

IndexTypeDesc fxcc::graph::opengl3::RenderStates::GetIndexDesc(const common::IndexType type)

{
	return m_IndexDescs[(int)type];
}

ModeTypeDesc fxcc::graph::opengl3::RenderStates::GetModeDesc(const common::ModeType &type)
{
	return m_ModeDescs[(int)type];
}

fxcc::graph::opengl3::SamplerDesc fxcc::graph::opengl3::RenderStates::GetSamplerLinearClamp()
{
	fxcc::graph::opengl3::SamplerDesc res;
	res.magFilter = GL_LINEAR;
	res.minFilter = GL_LINEAR;
	res.wrapR = GL_CLAMP_TO_EDGE;
	res.wrapS = GL_CLAMP_TO_EDGE;
	res.wrapT = GL_CLAMP_TO_EDGE;

	return res;
}

void fxcc::graph::opengl3::RenderStates::SetSampler(int target, const SamplerType &type)
{
	const auto &samp = m_SamplerDescs[(int)type].m_SampDesc;
	samp.BindTexture(target);
}

fxcc::graph::opengl3::AttachmentTypeDesc fxcc::graph::opengl3::RenderStates::GetAttachmentDesc(const fxcc::graph::common::AttachmentType &type)
{

	return m_AttachmentDescs[(int)type];
}
std::shared_ptr<fxcc::graph::opengl3::IBuffers> fxcc::graph::opengl3::RenderStates::GetBuffers()
{
	return Inst().m_Buffers;
}
;
fxcc::graph::opengl3::InputLayout fxcc::graph::opengl3::RenderStates::SkinnedVertexLayout()
{
	std::vector<fxcc::graph::opengl3::VertexAttr> layouts = {
		{0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, pos)},
		{1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, normal)},
		{2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, tex)},
		{3, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, tangent)},
		{4, 3, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, bigTangent)},
		{5, 4, GL_INT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, bones1)},
		{6, 4, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, weights1)},
		{7, 4, GL_INT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, bones2)},
		{8, 4, GL_FLOAT, GL_FALSE, sizeof(VertexSkinned), (void *)offsetof(VertexSkinned, weights2)}};
	return layouts;
}


fxcc::graph::opengl3::InputLayout fxcc::graph::opengl3::RenderStates::FixedVertexLayout()
{
	std::vector<fxcc::graph::opengl3::VertexAttr> layouts = {
		{0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFixed), (void *)offsetof(VertexFixed, pos)},
		{1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFixed), (void *)offsetof(VertexFixed, normal)},
		{2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexFixed), (void *)offsetof(VertexFixed, tex)},
		{3, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFixed), (void *)offsetof(VertexFixed, tangent)},
		{4, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFixed), (void *)offsetof(VertexFixed, bigTangent)}};

	return layouts;
}

void fxcc::graph::opengl3::RenderStates::Init()
{

	InitBlendDesc();

	InitRasterizerDesc();

	InitDepthStencilDesc();
	// INt Vertex Index and modeDesc
	InitVertexDesc();

	InitIndexDesc();

	InitModeDesc();

	InitDataTypeDesc();

	// Init Open gl Format
	InitFormatDesc();

	/// INt samper desc
	InitSamplerDesc();

	// init attachmentType Desc
	InitAttachmentType();

	InitBuffers();
}
void fxcc::graph::opengl3::RenderStates::InitBuffers()
{
	m_Buffers = std::make_shared<fxcc::graph::opengl3::IBuffers>();
}
;

void fxcc::graph::opengl3::RenderStates::InitAttachmentType()
{
	{
		auto &desc = m_AttachmentDescs[(int)fxcc::graph::common::AttachmentType::_None_];
		desc.m_Name = "none";
	}
	{
		auto &desc = m_AttachmentDescs[(int)fxcc::graph::common::AttachmentType::_Depth24_Stencil8_];
		desc.m_Attachment = GL_DEPTH_STENCIL_ATTACHMENT;
		desc.m_Component = GL_DEPTH24_STENCIL8;
		desc.m_Format = GL_DEPTH_STENCIL;
		desc.m_Name = "Depth24 stencil8";
	}
	{
		auto &desc = m_AttachmentDescs[(int)fxcc::graph::common::AttachmentType::_Depth_];
		desc.m_Attachment = GL_DEPTH_ATTACHMENT;
		desc.m_Component = GL_DEPTH_COMPONENT;
		desc.m_Format = GL_DEPTH_COMPONENT;

		desc.m_Name = "Depth";
	}
}
void fxcc::graph::opengl3::RenderStates::InitBlendDesc()
{
	{
		auto& bsFontRender = m_BlendDesc[(int)BlendType::_FONT_];
		bsFontRender.m_EnableBlend = true;
		bsFontRender.m_Sfactor = GL_SRC_ALPHA;
		bsFontRender.m_Dfactor = GL_ONE_MINUS_SRC_ALPHA;

	}
	{
		auto& desc = m_BlendDesc[(int)BlendType::_NONE_];
		desc.m_EnableBlend = false;
	}
}

void fxcc::graph::opengl3::RenderStates::InitRasterizerDesc()
{
	{
		auto &desc = m_RasterizerDesc[(int)RasterizerType::_NONE_];
		desc.m_EnableCullface = false;
	}
	{
		auto &desc = m_RasterizerDesc[(int)RasterizerType::_CULL_BACK_CCW_];
		desc.m_EnableCullface = true;
		desc.m_FrontFace = GL_CCW;
		desc.m_Cullface = GL_BACK;
	}
}

void fxcc::graph::opengl3::RenderStates::InitDepthStencilDesc()
{

	{
		auto &desc = m_DepthStencilDesc[(int)DepthStencilType::_DEPTH_LESS_STENCIL_ALAWAYS_WRITE];
		desc.m_EnableDepthFunc = true;
		desc.m_DepthFunc = GL_LESS;

		desc.m_EnableStencilFunc = true;
		desc.m_StencilFunc = GL_ALWAYS;

		desc.m_StencilFail = GL_KEEP;
		desc.m_DepthFail = GL_KEEP;
		desc.m_DepthStencilPass = GL_REPLACE;
		desc.m_StencilRef = 1;
		desc.m_StencilRefMask = 0xFF;
		desc.m_StencilWriteMask = 0xFF;
	}

	{

		auto &desc = m_DepthStencilDesc[(int)DepthStencilType::_DEPTH_LESS_STENCIL_NONE];
		desc.m_EnableDepthFunc = true;
		desc.m_DepthFunc = GL_LESS;

		desc.m_EnableStencilFunc = false;
	}
	{

		auto& desc = m_DepthStencilDesc[(int)DepthStencilType::_DEPTH_LEQUAL_STENCIL_NONE];
		desc.m_EnableDepthFunc = true;
		desc.m_DepthFunc = GL_LEQUAL;

		desc.m_EnableStencilFunc = false;
	}

	{
		auto &desc = m_DepthStencilDesc[(int)DepthStencilType::_DEPTH_LESS_STENCIL_ONLY_STENCIL_PASS];

		desc.m_EnableDepthFunc = false;
		desc.m_EnableStencilFunc = true;
		desc.m_StencilFunc = GL_NOTEQUAL;
		desc.m_StencilRef = 1;
		desc.m_StencilRefMask = 0xFF;
	}
	{
		auto &desc = m_DepthStencilDesc[(int)DepthStencilType::_DEPTH_NONE_STENCIL_NONE_];

		desc.m_EnableDepthFunc = false;
		desc.m_EnableStencilFunc = false;
	}
}

void fxcc::graph::opengl3::RenderStates::InitVertexDesc()
{
	m_VertexDescs[(int)VertexType::_vertex_none_] = {"None", 0, {}};
	m_VertexDescs[(int)VertexType::_vertex_fixed_] = {"VertexFixed", sizeof(VertexFixed), RenderStates::FixedVertexLayout()};
	m_VertexDescs[(int)VertexType::_vertex_skinned_] = {"VertexSkinned", sizeof(VertexSkinned),
														RenderStates::SkinnedVertexLayout()};
}

void fxcc::graph::opengl3::RenderStates::InitIndexDesc()
{
	m_IndexDescs[(int)IndexType::_index_none_] = {"None", GL_NONE, 0};
	m_IndexDescs[(int)IndexType::_index_unsigned_byte_] = {"unsigned byte", GL_UNSIGNED_BYTE, 1};
	m_IndexDescs[(int)IndexType::_index_unsigned_short_] = {"unsigned short", GL_UNSIGNED_SHORT, 2};
	m_IndexDescs[(int)IndexType::_index_unsigned_int_] = {"unsigned int", GL_UNSIGNED_INT, 4};
}

void fxcc::graph::opengl3::RenderStates::InitModeDesc()
{
	m_ModeDescs[(int)ModeType::_mode_none_] = {"None", 0};
	m_ModeDescs[(int)ModeType::_mode_Triangles_] = {"Triangle", GL_TRIANGLES};
	// m_ModeDescs[(int)ModeType::_mode_Triangle_fan_] = { "Triangle Fan", GL_TRIANGLE_FAN };
	m_ModeDescs[(int)ModeType::_mode_Triangle_stripe_] = {"Triangle Stripe", GL_TRIANGLE_STRIP};
	m_ModeDescs[(int)ModeType::_mode_Lines_] = {"Lines", GL_LINES};
	m_ModeDescs[(int)ModeType::_mode_Line_Stripe] = {"Line Stripe", GL_LINE_STRIP};
	// m_ModeDescs[(int)ModeType::_mode_Line_Loop] = { "Line Loop", GL_LINE_LOOP };
	m_ModeDescs[(int)ModeType::_mode_points_] = {"Points ", GL_POINTS};
}

void fxcc::graph::opengl3::RenderStates::InitDataTypeDesc()
{
	// Init DataType Desc
	m_DataTypeDescs[(int)common::DataType::_dataType_none_] = {"None", GL_NONE};
	m_DataTypeDescs[(int)common::DataType::_dataType_float_] = {"Float", GL_FLOAT};
	m_DataTypeDescs[(int)common::DataType::_dataType_Unsigned_byte_] = {"Unsigned Byte", GL_UNSIGNED_BYTE};
	m_DataTypeDescs[(int)common::DataType::_dataType_Unsigned_short_] = {"Unsigned Short", GL_UNSIGNED_SHORT};
	m_DataTypeDescs[(int)common::DataType::_dataType_Unsigned_int_] = {"Unsigned Int", GL_UNSIGNED_INT};
	m_DataTypeDescs[(int)common::DataType::_dataType_byte_] = {"Byte", GL_BYTE};
	m_DataTypeDescs[(int)common::DataType::_dataType_short_] = {"Short", GL_SHORT};
	m_DataTypeDescs[(int)common::DataType::_dataType_int_] = {"Int", GL_INT};
}

void fxcc::graph::opengl3::RenderStates::InitFormatDesc()
{
	m_FormatDescs[0] = {"None", 0, 0, 0, 0};
	m_FormatDescs[1] = {"R", GL_RED, GL_RED, GL_R16F ,GL_R32F};
	m_FormatDescs[2] = {"RG", GL_RG, GL_RG, GL_RG16F, GL_RG32F};
	m_FormatDescs[3] = {"RGB", GL_RGB, GL_RGB, GL_RGB16F , GL_RGB32F};
	m_FormatDescs[4] = {"RGBA", GL_RGBA, GL_RGBA, GL_RGBA16F, GL_RGBA32F};
};

void fxcc::graph::opengl3::RenderStates::InitSamplerDesc()
{
	m_SamplerDescs[(int)SamplerType::_sampler_none_] = {"texture none", RenderStates::GetSamplerLinearClamp()};
	m_SamplerDescs[(int)SamplerType::_sampler_2d_default_] = {"texture 2d default", RenderStates::GetSamplerLinearClamp()};
	m_SamplerDescs[(int)SamplerType::_sampler_cube_default_] = {"texture Cube default", RenderStates::GetSamplerLinearClamp()};
	m_SamplerDescs[(int)SamplerType::_sampler_2d_array_default] = {"texture 2d array default", RenderStates::GetSamplerLinearClamp()};

	{
		auto &desc = m_SamplerDescs[(int)SamplerType::_sampler_cube_mipmap_linear_];
		auto &sampDesc = desc.m_SampDesc;
		desc.m_Name = "_sampler_cube_mipmap_linear_";
		sampDesc.wrapR = GL_CLAMP_TO_EDGE;
		sampDesc.wrapS = GL_CLAMP_TO_EDGE;
		sampDesc.wrapT = GL_CLAMP_TO_EDGE;
		//res.minFilter = GL_LINEAR;
		sampDesc.minFilter = GL_LINEAR_MIPMAP_LINEAR;
		sampDesc.magFilter = GL_LINEAR;

		//res.magFilter = GL_LINEAR;
		//res.minFilter = GL_LINEAR;
		//res.wrapR = GL_CLAMP_TO_EDGE;
		//res.wrapS = GL_CLAMP_TO_EDGE;
		//res.wrapT = GL_CLAMP_TO_EDGE;
	}

	{
		auto &desc = m_SamplerDescs[(int)SamplerType::_sampler_2d_nearest];
		auto &sampDesc = desc.m_SampDesc;
		sampDesc.wrapR = GL_CLAMP_TO_EDGE;
		sampDesc.wrapS = GL_CLAMP_TO_EDGE;
		sampDesc.minFilter = GL_NEAREST;
		sampDesc.magFilter = GL_NEAREST;
	}
	{
		auto &desc = m_SamplerDescs[(int)SamplerType::_sampler_2d_nearest_no_wrap];
		auto &sampDesc = desc.m_SampDesc;
		sampDesc.minFilter = GL_NEAREST;
		sampDesc.magFilter = GL_NEAREST;
	}

	{
		auto &desc = m_SamplerDescs[(int)SamplerType::_sampler_2d_nearest_repeat];
		auto &sampDesc = desc.m_SampDesc;
		sampDesc.minFilter = GL_NEAREST;
		sampDesc.magFilter = GL_NEAREST;
		sampDesc.wrapR = GL_REPEAT;
		sampDesc.wrapS = GL_REPEAT;
		sampDesc.wrapT = GL_REPEAT;
	}
	{
		auto& desc = m_SamplerDescs[(int)SamplerType::_sampler_3d_default];
		auto& sampDesc = desc.m_SampDesc;
		sampDesc.minFilter = GL_NEAREST;
		sampDesc.magFilter = GL_NEAREST;
		sampDesc.wrapR = GL_CLAMP_TO_EDGE;
		sampDesc.wrapS = GL_CLAMP_TO_EDGE;
		sampDesc.wrapT = GL_CLAMP_TO_EDGE;
		sampDesc = GetSamplerLinearClamp();
	}
};

void fxcc::graph::opengl3::RenderStates::Clear(const fxcc::graph::common::RenderStateDesc &type)
{
	{
		const auto &desc = m_DepthStencilDesc[(int)type.m_DepthStencilType];
		desc.Use();
	}

	{
		const auto &desc = m_BlendDesc[(int)type.m_BlendType];
		desc.Use();
	}

	{
		const auto &desc = m_RasterizerDesc[(int)type.m_RasterizerType];
		desc.Use();
	}
}

// std::vector<Texture2DDescTypeDesc> fxcc::graph::opengl3::RenderStates::m_Texture2DDescTypeDesc =
//{
//	{"None"},
//	{"Origin Data"},
//	{"Image Desc"},
//	{"Origin Data File"},
//
// };

// std::vector<ShaderDescTypeDesc> fxcc::graph::opengl3::RenderStates::m_ShaderDescTypeDesc =
//{
//	{"None"},
//	{"Shader Code"},
//	{"Shader File"} };
//
// std::vector<TextureMaterialTypeDesc> fxcc::graph::opengl3::RenderStates::m_TextureMatTypeDesc =
//{
//	{"_Texture_Mat_NONE"},
//	{"_Texture_Mat_DIFFUSE"},
//	{"_Texture_Mat_SPECULAR"},
//	{"_Texture_Mat_AMBIENT"},
//	{"_Texture_Mat_EMISSIVE"},
//	{"_Texture_Mat_HEIGHT"},
//	{"_Texture_Mat_NORMALS"},
//	{"_Texture_Mat_SHININESS"},
//	{"_Texture_Mat_OPACITY"},
//	{"_Texture_Mat_DISPLACEMENT"},
//	{"_Texture_Mat_LIGHTMAP"},
//	{"_Texture_Mat_REFLECTION"},
//	{"_Texture_Mat_BASE_COLOR"},
//	{"_Texture_Mat_NORMAL_CAMERA"},
//	{"_Texture_Mat_EMISSION_COLOR"},
//	{"_Texture_Mat_METALNESS"},
//	{"_Texture_Mat_DIFFUSE_ROUGHNESS"},
//	{"_Texture_Mat_AMBIENT_OCCLUSION"},
//	{"_Texture_Mat_UNKNOWN"},
//	{"_Texture_Mat_End"},
//
// };